package lzupdater.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Vector;
import java.util.Map.Entry;

import lzupdater.com.CommunicationHub;
import lzupdater.com.HubComponent;
import lzupdater.gui.listeners.ProfileListener;
import lzupdater.locale.Dictionary;
import lzupdater.locale.LocalizedString;
import lzupdater.log.Logger;
import lzupdater.properties.PropertyElement;
import lzupdater.properties.PropertyElement.HashMapProperty;
import lzupdater.utils.Profile.ProfilePropertyId;

//LZRosterUpdater, a java client to update LZRoster
//Copyright (C) 2008 Fraskor from Krasus (e-mail: fraskor@gmail.com)
//
//This file is part of LZRosterUpdater.
//
//LZRosterUpdater is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Profile manager. Keeps track of all the profiles in the application.
 * 
 * @author Fraskor
 */
public class LZUProfileManager extends HubComponent {

	private final static LocalizedString noProfileSelected = new LocalizedString("No profile selected");
	private final static LocalizedString setCurrentProfile = new LocalizedString("Set profile '%s' as current profile");
	private final static LocalizedString profileNameAlreadyExists = new LocalizedString("The profile name '%s' already exists !!");
	private final static LocalizedString profileRenamed = new LocalizedString("Profile '%s' has been renamed to '%s'");
	private final static LocalizedString profileCouldNotBeFoundForDeletion = new LocalizedString("The profile '%s' could not be found for deletion");
	private final static LocalizedString profileDelete = new LocalizedString("Profile '%s' has been deleted");

	private Profile currentProfile = null;
	private HashMap<String, Profile> profiles = new HashMap<String, Profile>();

	private ArrayList<ProfileListener> profileListeners = new ArrayList<ProfileListener>();

	private Logger logger = null;

	public LZUProfileManager(CommunicationHub hub) {
		super(hub);
	}

	@Override
	public void initializeCommunication() {
		logger = hub.getComponent(LZULogger.class);
	}

	public void setDefault() {
		profiles.clear();
//		Profile p = new Profile(logger);
//		p.setName("default");
//		profiles.put(p.getName(), p);
//		setCurrentProfile("default");
	}

	public void load(PropertyElement element) {
		setCurrentProfile(null);
		profiles.clear();

		HashMapProperty map = (element == null) ? null : element
				.toHashMapProperty();

		if (map == null) {
			setDefault();
		} else {

			for (Entry<String, PropertyElement> entry : map) {
				String profileName = entry.getKey();
				HashMapProperty subMap = entry.getValue().toHashMapProperty();
				Profile newProfile = new Profile(logger);

				// Initialize all properties of the new profile
				for (ProfilePropertyId propertyId : ProfilePropertyId.values()) {
					String value = subMap.getAsString(propertyId.getPath(),
							propertyId.getDefaultValue());

					if (propertyId.isEncryptedValue()) {
						value = Encrypter.decryptString(value);
					}

					newProfile.setProperty(propertyId, value);
				}

				newProfile.setName(profileName);
				profiles.put(profileName, newProfile);

			}
		}

		fireProfileListUpdate();
		if (currentProfile == null && profiles.size() != 0) {
			setCurrentProfile(profiles.keySet().iterator().next());
		}
	}

	private LinkedList<ProfileManagerListener> profileManagerListeners = new LinkedList<ProfileManagerListener>();

	public void addProfileManagerListener(ProfileManagerListener listener) {
		if (!profileManagerListeners.contains(listener)) {
			profileManagerListeners.add(listener);
			listener.setProfileList(profiles.keySet().toArray());
		}
	}

	private void fireProfileListUpdate() {
		for (ProfileManagerListener pl : profileManagerListeners) {
			pl.setProfileList(profiles.keySet().toArray());
		}
	}

	public void save(HashMapProperty map) {
		map.clear();

		for (Entry<String, Profile> entry : profiles.entrySet()) {
			String path = entry.getKey();
			Profile profile = entry.getValue();

			for (ProfilePropertyId propertyId : ProfilePropertyId.values()) {
				if (propertyId == ProfilePropertyId.PASSWORD
						&& !profile.isSavePassword())
					continue;
				if (propertyId == ProfilePropertyId.NAME)
					continue;

				String value = profile.getProperty(propertyId);

				if (propertyId.isEncryptedValue()) {
					value = Encrypter.encryptString(value);
				}

				map.addProperty(String.format("%s.%s", path, propertyId
						.getPath()), value

				);
			}
		}
	}

	public void setCurrentProfile(String profileName) {
		Profile oldProfile = currentProfile;

		currentProfile = null;
		if (profileName != null) {
			currentProfile = profiles.get(profileName);
		}
		if (oldProfile != currentProfile) {
			logger.debug(setCurrentProfile
					.useAsFormater(currentProfile == null ? "" : currentProfile
							.getName()));
			fireProfileChange(currentProfile);
		}
	}

	public Profile getCurrentProfile() {
		return currentProfile;
	}

	private void fireProfileChange(Profile newProfile) {
		for (ProfileListener listener : profileListeners) {
			listener.setProfile(newProfile);
		}
	}

	public void addProfileListener(ProfileListener profileListener) {
		if (profileListener != null
				&& !profileListeners.contains(profileListener)) {
			profileListeners.add(profileListener);
		}
	}

	public void addProfileListener(ProfileListener[] profileListeners) {
		for (ProfileListener pl : profileListeners) {
			addProfileListener(pl);
		}
	}

	public boolean checkRenaming(String newName) {
		return !profiles.containsKey(newName);
	}

	public void deleteProfile(String name) {
		if (!profiles.containsKey(name)) {
			logger.error(profileCouldNotBeFoundForDeletion.useAsFormater(name));
			return;
		}
		String newProfile;

		if (profiles.keySet().size() == 1) {
			newProfile = null;
		} else {
			Vector<String> names = new Vector<String>(profiles.keySet().size());
			for (String s : profiles.keySet())
				names.add(s);
			Collections.sort(names);

			int i = 0;
			for (i = 0; i < names.size(); i++) {
				if (names.get(i).equals(name))
					break;
			}
			if (i == names.size() - 1) {
				newProfile = names.get(i - 1);
			} else {
				newProfile = names.get(i + 1);
			}
		}
		setCurrentProfile(newProfile);
		profiles.remove(name);
		fireProfileListUpdate();
		logger.info(profileDelete.useAsFormater(name));
	}

	public void renameCurrentProfile(String newName) {
		if (currentProfile == null) {
			logger.error(noProfileSelected);
		} else if (profiles.containsKey(newName)) {
			logger.error(profileNameAlreadyExists.useAsFormater(newName));
		} else {
			String oldName = currentProfile.getName();

			profiles.put(newName, currentProfile);
			profiles.remove(oldName);

			currentProfile.setName(newName);

			fireProfileListUpdate();
			fireProfileChange(currentProfile);

			logger.info(profileRenamed.useAsFormater(oldName, newName));
		}
	}

	public void cloneProfile(String name) {
		Profile ref = profiles.get(name);
		if (ref == null) {
			addNewProfile();
		} else {
			addProfile(name, new Profile(logger).clone(ref));
		}
	}

	public void addNewProfile() {
		addProfile(Dictionary.translate("new"), new Profile(logger));
	}

	private void addProfile(String baseName, Profile p) {
		int idx = 1;
		String name = baseName;
		while (profiles.containsKey(name)) {
			name = String.format("%s%d", baseName, ++idx);
		}

		p.setName(name);
		profiles.put(p.getName(), p);
		// setCurrentProfile(name);
		fireProfileListUpdate();
	}
}
